
#include <swarm.h>
#include <swarmio.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


void _swio_die_double_conv(char *num){

  printf("[swarmio] Error converting '%s' to a floating point number.\n", num);
  printf("[swarmio]   File: %s, line: %d\n",
	 files->top->name, files->top->current_line);
  sw_exit(1);

}

char *_swio_real_path(char *include, swio_file_frame *frame){

  char *cpy = malloc(strlen(files->top->name) + 1);
  memcpy(cpy, files->top->name, strlen(files->top->name) + 1);
  
  char *end = &(cpy[strlen(cpy)]);

  while (end >= cpy){
    if (*end == '/'){
      *(end+1) = 0;
      break;
    }
    end--;
  }

  char *path = (char *)malloc(strlen(cpy) + strlen(include) + 1); 
  memset(path, 0, strlen(cpy) + strlen(include) + 1);

  memcpy(path, cpy, strlen(cpy));
  memcpy(path + strlen(cpy), include, strlen(include));

  free(cpy);

  return path;

}

/**
 * Return one on success.
 */
int swio_com_include(char *line){

  char *include = line;
  while (*include++ != ' ');
  while (*include == ' ') include++;

  if (include[0] != '"' || include[strlen(include) - 1] != '"'){
    printf("[swarmio] Error while including file: %s\n", include);
    printf("[swarmio]   File: %s, line: %d\n",
	   files->top->name, files->top->current_line);
    printf("[swarmio]   Expected '\"' however not enough where found.\n");
    return 1;
  }

  /* We have to trim quotes. */
  char *clean_include = _swio_trim_quotes(include);

  /* Determine what the real path to the included file is. */
  char *include_path = _swio_real_path(clean_include, files->top);

  /* Make the file frame and push it onto the file stack. */
  swio_file_frame *new_file = _swio_make_file_frame(include_path);
  if (!new_file){
    printf("[swarmio] Error while including file: %s\n", include_path);
    printf("[swarmio]   File: %s, line: %d\n",
	   files->top->name, files->top->current_line);
    printf("[swarmio]   Unable to open file: %s\n", include_path);
    return 1;
  }

  _swio_file_stack_push(new_file);

  return 0;

}

int swio_com_sim_name(char *line, sw_swarm *sw){

  char *sim_name = line;
  while (*sim_name++ != ' ');
  while (*sim_name == ' ') sim_name++;

  if (sim_name[0] != '"' || sim_name[strlen(sim_name) - 1] != '"'){
    printf("[swarmio] Error while setting swarm name: %s\n", sim_name);
    printf("[swarmio]   File: %s, line: %d\n",
	   files->top->name, files->top->current_line);
    printf("[swarmio]   Expected '\"' however not enough where found.\n");
    return 1;
  }
  char *clean_sim_name = _swio_trim_quotes(sim_name);

  char *name = (char *)malloc(strlen(clean_sim_name) + 1);
  memcpy(name, clean_sim_name, strlen(clean_sim_name));
  sw->name = name;

  return 0;

}

/* Attribute parsing functions. */

void _swio_die_bad_attr(char *attr){

  int i = 0;
  for ( ; i < strlen(attr); i++){
    if (attr[i] == '='){
      attr[i] = 0;
      break;
    }
  }

  printf("[swarmio] Error while parsing attribute: %s\n", attr);
  printf("[swarmio]   File: %s, line: %d\n",
	 files->top->name, files->top->current_line);
  printf("[swarmio]   Badly formed attribute.\n");
  sw_exit(1);

}

void _swio_die_unknown_attr(char *attr){

  int i = 0;
  for ( ; i < strlen(attr); i++){
    if (attr[i] == '='){
      attr[i] = 0;
      break;
    }
  }

  printf("[swarmio] Error while parsing attribute: %s\n", attr);
  printf("[swarmio]   File: %s, line: %d\n",
	 files->top->name, files->top->current_line);
  printf("[swarmio]   Unknown attribute.\n");
  sw_exit(1);

}

char **attrs;

void _swio_init_attr_list(){

  attrs = (char **) malloc(sizeof(char *) * NUM_ATTRS);

  attrs[ATTR_ORIGIN]    = "origin";
  attrs[ATTR_SIZE]      = "size";
  attrs[ATTR_NAME]      = "name";
  attrs[ATTR_ORDER]     = "order";
  attrs[ATTR_MAGNITUDE] = "magnitude";
  attrs[ATTR_TYPE]      = "type";
  attrs[ATTR_FEMIT]     = "femit";
  attrs[ATTR_FRESPOND]  = "frespond";
  attrs[ATTR_LOC]       = "loc";
  attrs[ATTR_VEL]       = "vel";
  attrs[ATTR_QUANTITY]  = "quantity";
  attrs[ATTR_COLOR]     = "color";
  attrs[ATTR_MIN]       = "min";
  attrs[ATTR_MAX]       = "max";

}

int _swio_attr_type(char *attr){

  if (!attrs){
    _swio_init_attr_list();
  }

  int i = 0;
  for ( ; i < NUM_ATTRS; i++){
    if (!strncmp(attr, attrs[i], strlen(attrs[i])))
      return i;
  }
  
  return -1;

}

int _swio_parse_string(char *string, char **dest){

  char *copied_data;

  // If there are surrounding quotes remove them.
  if (string[0] == '"'){
    char *tmp = _swio_trim_quotes(string);
    if (!tmp){
      swio_die_miss_matched_quote(string);
    }
    string = tmp;
  }

  // Now see if its a symbol or a literal.
  if (_swio_is_symbol(string) == 0){
    
    string++;
    swio_sym_data *data = swio_sym_get(string);
    if (!data){
      swio_undefined_symbol(string);
    }

    copied_data = (char *) malloc(strlen(data->data) + 1);
    memset(copied_data, 0, strlen(data->data) + 1);
    memcpy(copied_data, data->data, strlen(data->data) + 1);

  } else {
    
    copied_data = (char *)malloc(strlen(string) + 1);
    memset(copied_data, 0, strlen(string) + 1);
    memcpy(copied_data, string, strlen(string) + 1);

  }

  *dest = copied_data;

  return 0;

}

int _swio_parse_numeric(char *numeric, double *dest){

  // If there are surrounding quotes remove them.
  if (numeric[0] == '"'){
    char *tmp = _swio_trim_quotes(numeric);
    if (!tmp){
      swio_die_miss_matched_quote(numeric);
    }
    numeric = tmp;
  }
  
  // numeric is ethier a double literal or a symbol.
  if (strncmp("random", numeric, strlen(numeric)) == 0){

    /* Get a random num from the generator. */
    *dest = sw_random();

  } else if (_swio_is_symbol(numeric) == 0){

    numeric++;
    swio_sym_data *data = swio_sym_get(numeric);
    if (!data){
      swio_undefined_symbol(numeric);
    }
    *dest = *(double *)data->data;

  } else {

    int conv = sscanf(numeric, "%lf", dest);
    if (conv != 1) return 1;

  }

  return 0;

}

int _swio_parse_colon_sep_symbols(char *nums, double *dest){

  /* Parse two doubels from the colon seperated list passed and put */
  /* put them in the passed array.                                  */

  if (nums[0] == '"'){
    char *tmp = _swio_trim_quotes(nums);
    if (!tmp){
      swio_die_miss_matched_quote(nums);
    }
    nums = tmp;
  }

  // The two item vars will point to where the nums should be.
  char *item1 = nums;
  char *item2 = nums;
  while (*item2++ != ':'){
    if (*item2 == 0) return 1;
  }
  *(item2-1) = 0;

  if (_swio_parse_numeric(item1, dest++)){
    return 1;
  }
  if (_swio_parse_numeric(item2, dest)){
    return 1;
  }
  return 0;

}

int _swio_parse_attr_size(char *attr, sw_swarm *sw){

  /* Parse the size attribute. */
  if (strncmp(attr, attrs[ATTR_SIZE], strlen(attrs[ATTR_SIZE]))) return 1;

  // Find the equals.
  while (*attr++ != '='){
    if (*attr == 0) return 1;
  }

  double tmp[2];

  if (_swio_parse_colon_sep_symbols(attr, tmp)){
    return 1;
  }

  sw->size.x = tmp[0];
  sw->size.y = tmp[1];
  return 0;

}

int _swio_parse_attr_name(char *attr, sw_force *f){

  if (strncmp(attr, attrs[ATTR_NAME], strlen(attrs[ATTR_NAME]))) return 1;

  // Find the equals.
  while (*attr++ != '='){
    if (*attr == 0) return 1;
  }

  if (_swio_parse_string(attr, &f->name)){
    return 1;
  }

  return 0;

}

int _swio_parse_attr_order(char *attr, sw_force *f){

  if (strncmp(attr, attrs[ATTR_ORDER], strlen(attrs[ATTR_ORDER]))) return 1;

  // Find the equals.
  while (*attr++ != '='){
    if (*attr == 0) return 1;
  }

  if (_swio_parse_numeric(attr, &f->force_order)){
    return 1;
  }

  return 0;

}

int _swio_parse_attr_magnitude(char *attr, sw_force *f){

  if (strncmp(attr, attrs[ATTR_MAGNITUDE], strlen(attrs[ATTR_MAGNITUDE]))) 
    return 1;

  // Find the equals.
  while (*attr++ != '='){
    if (*attr == 0) return 1;
  }

  if (_swio_parse_numeric(attr, &f->force_constant)){
    return 1;
  }

  return 0;

}

int _swio_parse_attr_type(char *attr, swio_insect_desc *ins){

  if (strncmp(attr, attrs[ATTR_TYPE], strlen(attrs[ATTR_TYPE]))) return 1;

  // Find the equals.
  while (*attr++ != '='){
    if (*attr == 0) return 1;
  }

  if (_swio_parse_string(attr, &ins->type)){
    return -1;
  }

  return 0;

}

int _swio_parse_attr_femit(char *attr, swio_insect_desc *ins, sw_swarm *sw){

  if (strncmp(attr, attrs[ATTR_FEMIT], strlen(attrs[ATTR_FEMIT]))) return 1;

  // Find the equals.
  while (*attr++ != '='){
    if (*attr == 0) return 1;
  }

  if (attr[0] == '"'){
    attr = _swio_trim_quotes(attr);
  }

  char *force = strtok(attr, ",");

  while (force != NULL){
    if (swio_desc_add_eforce(ins, force, sw)){
      return 1;
    }
    /* Move to next comma. */
    force = strtok(NULL, ",");
  }

  return 0;

}

int _swio_parse_attr_frespond(char *attr, swio_insect_desc *ins, sw_swarm *sw){

  if (strncmp(attr, attrs[ATTR_FRESPOND], strlen(attrs[ATTR_FRESPOND])))
    return 1;

  // Find the equals.
  while (*attr++ != '='){
    if (*attr == 0) return 1;
  }

  if (attr[0] == '"'){
    attr = _swio_trim_quotes(attr);
  }

  char *force = strtok(attr, ",");

  while (force != NULL){
    if (swio_desc_add_rforce(ins, force, sw)){
      return 1;
    }
    force = strtok(NULL, ",");
  }

  return 0;

}

int _swio_parse_attr_loc(char *attr, swio_insect_desc *ins){

  if (strncmp(attr, attrs[ATTR_LOC], strlen(attrs[ATTR_LOC]))) return 1;

  // Find the equals.
  while (*attr++ != '='){
    if (*attr == 0) return 1;
  }

  double loc[2];
  if (_swio_parse_colon_sep_symbols(attr, loc)){
    return 1;
  }

  ins->loc.x = loc[0];
  ins->loc.y = loc[1];

  return 0;

}

int _swio_parse_attr_vel(char *attr, swio_insect_desc *ins){

  if (strncmp(attr, attrs[ATTR_VEL], strlen(attrs[ATTR_VEL]))) return 1;

  // Find the equals.
  while (*attr++ != '='){
    if (*attr == 0) return 1;
  }

  double vel[2];
  if (_swio_parse_colon_sep_symbols(attr, vel)){
    return 1;
  }

  ins->vel.x = vel[0];
  ins->vel.y = vel[1];

  return 0;

}

int _swio_parse_attr_quantity(char *attr, swio_insect_desc *ins){

  if (strncmp(attr, attrs[ATTR_QUANTITY], strlen(attrs[ATTR_QUANTITY])))
    return 1;

  // Find the equals.
  while (*attr++ != '='){
    if (*attr == 0) return 1;
  }

  double quan;

  if (_swio_parse_numeric(attr, &quan)){
    return 1;
  }

  ins->quantity = (int) quan;

  return 0;

}

int _swio_parse_attr_color(char *attr, swio_insect_desc *ins){

  if (strncmp(attr, attrs[ATTR_COLOR], strlen(attrs[ATTR_COLOR]))) return 1;

  // Find the equals.
  while (*attr++ != '='){
    if (*attr == 0) return 1;
  }

  if (_swio_parse_string(attr, &ins->color)){
    return 1;
  }

  return 0;

}

int swio_com_window(char *line, sw_swarm *sw){

  char *win_info = line;
  while (*win_info++ != ' ');
  while (*win_info == ' ') win_info++;

  /* win_info points to the start of the window info. */
  char *attr;
  attr = strtok(win_info, " ");

  while (attr){
    //printf("[swarmio] Attr: %s\n", attr);
    /* Parse the attribute. */
    switch (_swio_attr_type(attr)){
    case ATTR_SIZE: // Size
      if (_swio_parse_attr_size(attr, sw)){
	/* Error */
	_swio_die_bad_attr(attr);
      }
      break;
    default:
      _swio_die_unknown_attr(attr);
    }
    attr = strtok(NULL, " ");
  }

  return 0;

}

int swio_com_force(char *line, sw_swarm *sw){

  char *force_info = line;
  while (*force_info++ != ' ');
  while (*force_info == ' ') force_info++;

  /* force_info points to the start of the window info. */
  char *attr;
  attr = strtok(force_info, " "); 
  
  sw_force *f = sw_new_force();

  while (attr){
    //printf("[swarmio] Attr: %s\n", attr);
    switch (_swio_attr_type(attr)){
    case ATTR_NAME:
      if (_swio_parse_attr_name(attr, f)){
	_swio_die_bad_attr(attr);
      }
      break;
    case ATTR_ORDER:
      if (_swio_parse_attr_order(attr, f)){
	_swio_die_bad_attr(attr);
      }
      break;
    case ATTR_MAGNITUDE:
      if (_swio_parse_attr_magnitude(attr, f)){
	_swio_die_bad_attr(attr);
      }
      break;
    default:
      _swio_die_unknown_attr(attr);
    }
    attr = strtok(NULL, " ");
  }
  
  sw_swarm_add_force(sw, f);
  free(f);

  return 0;
  
}

int swio_com_insect(char *line, sw_swarm *sw){

  char *insect_info = line;
  while (*insect_info++ != ' ');
  while (*insect_info == ' ') insect_info++;

  /* insect_info points to the start of the window info. */
  char *saveptr;
  char *attr = strtok_r(insect_info, " ", &saveptr);
  swio_insect_desc ins;
  memset(&ins, 0, sizeof(ins));
  ins.quantity = 1;

  while (attr){
    //printf("[swarmio] Attr: %s\n", attr);
    switch (_swio_attr_type(attr)){
    case ATTR_TYPE:
      if (_swio_parse_attr_type(attr, &ins)){
	_swio_die_bad_attr(attr);
      }
      break;
    case ATTR_FEMIT:
      if (_swio_parse_attr_femit(attr, &ins, sw)){
	_swio_die_bad_attr(attr);
      }
      break;
    case ATTR_FRESPOND:
      if (_swio_parse_attr_frespond(attr, &ins, sw)){
	_swio_die_bad_attr(attr);
      }
      break;
    case ATTR_VEL:
      if (_swio_parse_attr_vel(attr, &ins)){
	_swio_die_bad_attr(attr);
      }
      break;
    case ATTR_LOC:
      if (_swio_parse_attr_loc(attr, &ins)){
	_swio_die_bad_attr(attr);
      }
      break;
    case ATTR_QUANTITY:
      if (_swio_parse_attr_quantity(attr, &ins)){
	_swio_die_bad_attr(attr);
      }
      break;
    case ATTR_COLOR:
      if (_swio_parse_attr_color(attr, &ins)){
	_swio_die_bad_attr(attr);
      }
      break;
    default:
      _swio_die_unknown_attr(attr);
    }
    attr = strtok_r(NULL, " ", &saveptr);
  }
  /*
  printf("[swarmio] Insect Desc:\n");
  printf("[swarmio]   Type=%s\n", ins.type);
  printf("[swarmio]   Loc=");
  sw_println_vec(&ins.loc);
  printf("[swarmio]   Vel=");
  sw_println_vec(&ins.vel);
  printf("[swarmio]   Emit Forces:\n");
  int i = 0;
  for ( ; i < ins.f_len_e; i++){
    sw_print_force(ins.femit[i]);
  }
  printf("[swarmio]   Respond Forces:\n");
  i = 0;
  for ( ; i < ins.f_len_r; i++){
    sw_print_force(ins.frespond[i]);
  }
  printf("[swarmio]   Insect Color: %s\n", ins.color);
  */
  /* Now turn the swarm desc into a set of insects. */
  swio_desc_to_insects(&ins, sw);

  return 0;

}

int _swio_parse_attr_min(char *attr, double *dest){

  if (strncmp(attr, attrs[ATTR_MIN], strlen(attrs[ATTR_MIN]))) return 1;

  // Find the equals.
  while (*attr++ != '='){
    if (*attr == 0) return 1;
  }
  
  if (_swio_parse_numeric(attr, dest)){
    return 1;
  }

  return 0;

}

int _swio_parse_attr_max(char *attr, double *dest){

  if (strncmp(attr, attrs[ATTR_MAX], strlen(attrs[ATTR_MAX]))) return 1;

  // Find the equals.
  while (*attr++ != '='){
    if (*attr == 0) return 1;
  }

  if (_swio_parse_numeric(attr, dest)){
    return 1;
  }

  return 0;

}

int swio_com_random(char *line){

  char *rand = line;
  while (*rand++ != ' ');
  while (*rand == ' ') rand++;

  char *attr = strtok(rand, " ");

  double min = 0;
  double max = 100;

  while (attr){
    switch (_swio_attr_type(attr)){
    case ATTR_MIN:
      if (_swio_parse_attr_min(attr, &min)){
	_swio_die_bad_attr(attr);
      }
      break;
    case ATTR_MAX:
      if (_swio_parse_attr_max(attr, &max)){
	_swio_die_bad_attr(attr);
      }
      break;
    default:
      _swio_die_unknown_attr(attr);
    }

    attr = strtok(NULL, " ");

  }

  sw_init_rand(min, max);

  return 0;

}
